import random
from web.forms.BootStrapForms import BootStrapModelForm, BootStrapForm
from utils.encrypt import md5
from web import models
from django import forms
# 导入正则表达式校验功能
from django.core.validators import RegexValidator
from django.core.exceptions import ValidationError
from utils.tencent.sms import send_sms_single
from django.conf import settings
from django_redis import get_redis_connection
from django.db.models import Q


class RegisterModelForm(BootStrapModelForm):
    """人员注册"""
    phone = forms.CharField(label='手机号', validators=[RegexValidator(r'^1[3-9]\d{9}$', '手机号格式错误！'), ])
    password = forms.CharField(label='密码', min_length=4, max_length=16, error_messages={
        'min_length': '密码长度至少4位',
        'max_length': '密码长度最多16位',
    }, widget=forms.PasswordInput(render_value=True))
    pass_confirm = forms.CharField(label='确认密码', min_length=4, max_length=16, error_messages={
        'min_length': '密码长度至少4位',
        'max_length': '密码长度最多16位',
    }, widget=forms.PasswordInput(render_value=True))
    code = forms.CharField(label='验证码', max_length=4)

    class Meta:
        model = models.Account
        fields = ['username', 'email', 'password', 'pass_confirm', 'phone', 'code']
        # fields = '__all__'

    # 对用户名重复进行验证
    def clean_username(self):
        txt_username = self.cleaned_data.get('username')
        exists = models.Account.objects.filter(username=txt_username).exists()
        print('123')
        print(self.cleaned_data)
        if exists:
            raise ValidationError('用户已存在!')
        return txt_username

    # 钩子函数对password进行加密
    def clean_password(self):
        txt_password = self.cleaned_data.get('password')
        encrypt_password = md5(txt_password)
        print(self.cleaned_data)
        return encrypt_password

    def clean_pass_confirm(self):
        txt_pass_confirm = self.cleaned_data.get('pass_confirm')
        encrypt_pass_confirm = md5(txt_pass_confirm)
        if encrypt_pass_confirm != self.cleaned_data.get('password'):
            raise ValidationError('两次输入的密码不一致！')
        return encrypt_pass_confirm

    def clean_phone(self):
        txt_phone = self.cleaned_data.get('phone')
        print(txt_phone, 1)
        exists = models.Account.objects.filter(phone=txt_phone).exists()
        if exists:
            raise ValidationError('号码已注册!')
        return txt_phone

    def clean_code(self):
        print(self.cleaned_data)
        txt_code = self.cleaned_data['code']
        # 因为phone有内置正则校验器，如果内置正则校验不通过，则self.cleaned_data中没有key=phone
        phone = self.cleaned_data.get('phone')
        if not phone:
            # 取不到电话号，结束code钩子函数，让内置正则校验器报错
            return txt_code

        # 如果取到phone
        conn = get_redis_connection('default')
        cache_code = conn.get(phone)
        if not cache_code:
            raise ValidationError('验证码失效，或发送失败!')

        if cache_code.decode().strip() != txt_code:
            raise ValidationError('验证码输入错误!')
        return txt_code


class SendSmsForm(forms.Form):
    """短信发送---使用钩子函数完成各类校验功能及调用腾讯云、redis存储"""
    # 校验手机号格式
    phone = forms.CharField(label='手机号', validators=[RegexValidator(r'^1[3-9]\d{9}$', '号码格式错误！')])

    # 通过重写init函数，将request传入类中
    def __init__(self, request, *args, **kwargs):
        super().__init__(*args, **kwargs)
        # 为类定义了个属性request，并且赋值为入参的request
        self.request = request

    def clean_phone(self):
        # 校验数据库中是否存在该手机号
        phone = self.cleaned_data['phone']
        exists = models.Account.objects.filter(phone=phone).exists()

        tpl = self.request.GET.get('type')
        if tpl == 'register':
            if exists:
                raise ValidationError('手机号已存在!')
        else:
            if not exists:
                raise ValidationError('手机号不存在!')
        # 发送短信
        code = random.randrange(1000, 9999)
        print(code)
        template_id = settings.TENCENT_TEMPLATE_ID.get(tpl)
        sms = send_sms_single(phone, template_id, [code, ])

        if sms['result'] != 0:
            raise ValidationError("短信发送失败，{}".format(sms['errmsg']))
        # 验证码写入redis
        conn = get_redis_connection('default')
        conn.set(phone, code, ex=60)
        return phone


class LoginSmsForm(BootStrapForm):
    """短信验证码登录"""
    phone = forms.CharField(label='手机号', validators=[RegexValidator(r'^1[3-9]\d{9}$', '手机号格式错误!')])
    code = forms.CharField(label='验证码', max_length=4, widget=forms.TextInput())

    def clean_phone(self):
        phone = self.cleaned_data.get('phone')
        user_obj = models.Account.objects.filter(phone=phone).first()
        if not user_obj:
            raise ValidationError('输入的手机号不存在!')
        return user_obj

    def clean_code(self):
        code = self.cleaned_data['code']
        phone = self.cleaned_data.get('phone').phone
        if not phone:
            return code
        conn = get_redis_connection('default')
        redis_code = conn.get(phone)
        if not redis_code:
            raise ValidationError('验证码失效或发送失败，请重试！')
        redis_str_code = redis_code.decode('utf-8')
        if code != redis_str_code:
            raise ValidationError('验证码输入错误！')
        return code


class LoginForm(BootStrapForm):
    username = forms.CharField(label='邮箱或手机号', max_length=32)
    password = forms.CharField(label='密码', widget=forms.PasswordInput(render_value=True))
    code = forms.CharField(label='验证码', widget=forms.TextInput)

    def __init__(self, request, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self.request = request

    def clean_username(self):
        username = self.cleaned_data['username']
        # 获取user_obj这个queryset回传回去方便后续视图中session存储
        user_obj = models.Account.objects.filter(Q(phone=username) | Q(email=username)).first()
        if not user_obj:
            raise ValidationError('输入的邮箱或手机号不存在!')
        return user_obj

    def clean_password(self):
        password = self.cleaned_data['password']
        username = self.cleaned_data.get('username')
        if not username:
            return password
        encrypt_password = md5(password)
        if encrypt_password != self.cleaned_data.get('username').password:
            raise ValidationError('密码错误!')
        return password

    def clean_code(self):
        code = self.cleaned_data['code']
        session_code = self.request.session.get('imgCode', None)
        if not session_code:
            raise ValidationError('验证码已失效，请刷新验证码！')
        if code.upper() != session_code:
            raise ValidationError('验证码输入错误，请重新输入！')
        return code
